﻿#region License
// MIT License
//
// Copyright(c) 2019-2025 ZhangYu
// https://gitee.com/zhangyu800/litebyte
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#endregion
#region Intro
// Purpose: Parse bytes to object
// Author: ZhangYu
// CreateDate: 2022-06-14
// LastModifiedDate: 2025-08-30
#endregion
using LiteByte.Converters;

namespace LiteByte {

    using LiteByte.Common;
    using LiteByte.Reflection;
    using System;
    using System.Collections;
    using System.Collections.Generic;

    /// <summary>
    /// <para> LiteByte 数据读取器</para>
    /// <para> 从字节中读取对象 | Read object from bytes</para>
    /// </summary>
    public class LBReader : BaseTypeReader {

        #region 构造方法 | Constructors
        public LBReader() : base() { }
        public LBReader(byte[] bytes) : base(bytes) { }
        #endregion

        #region 基本类型数组 | BaseType Array
        /// <summary> 基本类型数组| Base type array </summary>
        public Array ReadBaseTypeArray(TypeName baseType) {
            if (ReadBit1()) return null;
            int length = ReadVInt32();
            switch (baseType) {
                case TypeName.Bit1: return ReadBaseTypeArray(length, ReadBit1);
                case TypeName.Bit2: return ReadBaseTypeArray(length, ReadBit2);
                case TypeName.Bit3: return ReadBaseTypeArray(length, ReadBit3);
                case TypeName.Bit4: return ReadBaseTypeArray(length, ReadBit4);
                case TypeName.Bit5: return ReadBaseTypeArray(length, ReadBit5);
                case TypeName.Bit6: return ReadBaseTypeArray(length, ReadBit6);
                case TypeName.Bit7: return ReadBaseTypeArray(length, ReadBit7);
                case TypeName.Bit8: return ReadBaseTypeArray(length, ReadBit8);
                case TypeName.Bool: return ReadPrimitiveArray<bool>(length, 1);
                case TypeName.Char: return ReadPrimitiveArray<char>(length, 2);
                case TypeName.UInt8: return ReadPrimitiveArray<byte>(length, 1);
                case TypeName.UInt16: return ReadPrimitiveArray<ushort>(length, 2);
                case TypeName.UInt32: return ReadPrimitiveArray<uint>(length, 4);
                case TypeName.UInt64: return ReadPrimitiveArray<ulong>(length, 8);
                case TypeName.Int8: return ReadPrimitiveArray<sbyte>(length, 1);
                case TypeName.Int16: return ReadPrimitiveArray<short>(length, 2);
                case TypeName.Int32: return ReadPrimitiveArray<int>(length, 4);
                case TypeName.Int64: return ReadPrimitiveArray<long>(length, 8);
                case TypeName.Float16: return ReadBaseTypeArray(length, ReadFloat16);
                case TypeName.Float32: return ReadPrimitiveArray<float>(length, 4);
                case TypeName.Float64: return ReadPrimitiveArray<double>(length, 8);
                case TypeName.VUInt16: return ReadBaseTypeArray(length, ReadVUInt16);
                case TypeName.VUInt32: return ReadBaseTypeArray(length, ReadVUInt32);
                case TypeName.VUInt64: return ReadBaseTypeArray(length, ReadVUInt64);
                case TypeName.VInt16: return ReadBaseTypeArray(length, ReadVInt16);
                case TypeName.VInt32: return ReadBaseTypeArray(length, ReadVInt32);
                case TypeName.VInt64: return ReadBaseTypeArray(length, ReadVInt64);
                case TypeName.Decimal: return ReadBaseTypeArray(length, ReadDecimal);
                case TypeName.DateTime: return ReadBaseTypeArray(length, ReadDateTime);
                case TypeName.VUnicode: return ReadBaseTypeArray(length, ReadVUnicode);
                case TypeName.ASCII: return ReadBaseTypeArray(length, ReadASCII);
                case TypeName.UTF8: return ReadBaseTypeArray(length, ReadUTF8);
                case TypeName.UTF16: return ReadBaseTypeArray(length, ReadUTF16);
                case TypeName.UTF32: return ReadBaseTypeArray(length, ReadUTF32);
                case TypeName.GBK: return ReadBaseTypeArray(length, ReadGBK);
                default: return null;
            }
        }

        private T[] ReadBaseTypeArray<T>(int length, Func<T> readElement) {
            T[] array = new T[length];
            for (int i = 0; i < length; i++) {
                array[i] = readElement();
            }
            return array;
        }

        private T[] ReadPrimitiveArray<T>(int length, int elementSize) {
            T[] array = new T[length];
            int byteCount = length * elementSize;
            System.Buffer.BlockCopy(bytes, byteIndex, array, 0, byteCount);
            byteIndex += byteCount;
            return array;
        }
        #endregion

        #region 基本类型列表 | BaseType List
        /// <summary> 基本类型列表 | Base type list </summary>
        public IList ReadBaseTypeList(TypeName baseType) {
            if (ReadBit1()) return null;
            int count = ReadVInt32();
            switch (baseType) {
                case TypeName.Bit1: return ReadBaseTypeList(count, ReadBit1);
                case TypeName.Bit2: return ReadBaseTypeList(count, ReadBit2);
                case TypeName.Bit3: return ReadBaseTypeList(count, ReadBit3);
                case TypeName.Bit4: return ReadBaseTypeList(count, ReadBit4);
                case TypeName.Bit5: return ReadBaseTypeList(count, ReadBit5);
                case TypeName.Bit6: return ReadBaseTypeList(count, ReadBit6);
                case TypeName.Bit7: return ReadBaseTypeList(count, ReadBit7);
                case TypeName.Bit8: return ReadBaseTypeList(count, ReadBit8);
                case TypeName.Bool: return ReadBaseTypeList(count, ReadBool);
                case TypeName.Char: return ReadBaseTypeList(count, ReadChar);
                case TypeName.UInt8: return ReadBaseTypeList(count, ReadUInt8);
                case TypeName.UInt16: return ReadBaseTypeList(count, ReadUInt16);
                case TypeName.UInt32: return ReadBaseTypeList(count, ReadUInt32);
                case TypeName.UInt64: return ReadBaseTypeList(count, ReadUInt64);
                case TypeName.Int8: return ReadBaseTypeList(count, ReadInt8);
                case TypeName.Int16: return ReadBaseTypeList(count, ReadInt16);
                case TypeName.Int32: return ReadBaseTypeList(count, ReadInt32);
                case TypeName.Int64: return ReadBaseTypeList(count, ReadInt64);
                case TypeName.Float16: return ReadBaseTypeList(count, ReadFloat16);
                case TypeName.Float32: return ReadBaseTypeList(count, ReadFloat32);
                case TypeName.Float64: return ReadBaseTypeList(count, ReadFloat64);
                case TypeName.VUInt16: return ReadBaseTypeList(count, ReadVUInt16);
                case TypeName.VUInt32: return ReadBaseTypeList(count, ReadVUInt32);
                case TypeName.VUInt64: return ReadBaseTypeList(count, ReadVUInt64);
                case TypeName.VInt16: return ReadBaseTypeList(count, ReadVInt16);
                case TypeName.VInt32: return ReadBaseTypeList(count, ReadVInt32);
                case TypeName.VInt64: return ReadBaseTypeList(count, ReadVInt64);
                case TypeName.Decimal: return ReadBaseTypeList(count, ReadDecimal);
                case TypeName.DateTime: return ReadBaseTypeList(count, ReadDateTime);
                case TypeName.VUnicode: return ReadBaseTypeList(count, ReadVUnicode);
                case TypeName.ASCII: return ReadBaseTypeList(count, ReadASCII);
                case TypeName.UTF8: return ReadBaseTypeList(count, ReadUTF8);
                case TypeName.UTF16: return ReadBaseTypeList(count, ReadUTF16);
                case TypeName.UTF32: return ReadBaseTypeList(count, ReadUTF32);
                case TypeName.GBK: return ReadBaseTypeList(count, ReadGBK);
                default: return null;
            }
        }

        private List<T> ReadBaseTypeList<T>(int count, Func<T> readElement) {
            List<T> list = new List<T>(count);
            for (int i = 0; i < count; i++) {
                list.Add(readElement());
            }
            return list;
        }
        #endregion

        #region 读取对象 | Read Object
        /// <summary> 读取任意类型 | Read any type </summary>
        public T ReadObject<T>() {
            return (T)ReadObject(typeof(T), ConfigData.GetType(typeof(T).Name));
        }

        /// <summary> 读取任意类型 | Read any type </summary>
        public object ReadObject(Type type, TypeData lbType) {
            if (lbType.IsBaseType) return ReadBaseType(lbType.TypeCode);
            if (lbType.IsArray) return ReadArray(type, lbType);
            if (lbType.IsList) return ReadList(type, lbType);
            if (lbType.IsHashMap) return ReadHashMap(type, lbType);
            return ReadCustom(type, lbType);
        }

        /// <summary> 读取数组 | Read array </summary>
        public T[] ReadArray<T>() {
            Type type = typeof(T[]);
            TypeData elementType = ConfigData.GetType(typeof(T).Name);
            TypeData lbType = new TypeData(type.Name, elementType);
            return (T[])ReadArray(type, lbType);
        }

        /// <summary> 读取数组 | Read array </summary>
        private Array ReadArray(Type type, TypeData lbType) {
            // 读取元素 | Read elements
            TypeData lbElementType = lbType.ElementType;
            if (lbElementType.IsBaseType) {
                return ReadBaseTypeArray(lbElementType.TypeCode);
            } else {
                // 检查对象是否为默认值 | Check if object is default value
                if (ReadBit1()) return null;
                int length = (int)ReadVUInt32();
                Type elementType = type.GetElementType();
                Array array = ReflectionUtil.CreateArray(elementType, length);
                for (int i = 0; i < length; i++) {
                    array.SetValue(ReadObject(elementType, lbElementType), i);
                }
                return array;
            }
        }

        /// <summary> 读取列表 | Read list </summary>
        public List<T> ReadList<T>() {
            Type type = typeof(List<T>);
            List<TypeData> genericTypes = [ConfigData.GetType(typeof(T).Name)];
            TypeData lbType = new TypeData(type.Name, genericTypes, true, false);
            return (List<T>)ReadList(type, lbType);
        }

        /// <summary> 读取列表 | Read list </summary>
        private IList ReadList(Type type, TypeData lbType) {
            // 读取元素 | Read elements
            TypeData lbGenericType = lbType.GenericTypes[0];
            if (lbGenericType.IsBaseType) {
                return ReadBaseTypeList(lbGenericType.TypeCode);
            } else {
                // 检查对象是否为默认值 | Check if object is default value
                if (ReadBit1()) return null;
                int count = (int)ReadVUInt32();
                Type genericType = type.GenericTypeArguments[0];
                IList list = ReflectionUtil.CreateList(genericType, count);
                for (int i = 0; i < count; i++) {
                    list.Add(ReadObject(genericType, lbGenericType));
                }
                return list;
            }
        }

        /// <summary> 读取哈希表 | Read hash map </summary>
        public Dictionary<K, V> ReadHashMap<K, V>() {
            Type type = typeof(Dictionary<K, V>);
            List<TypeData> genericTypes = [ConfigData.GetType(typeof(K).Name), ConfigData.GetType(typeof(V).Name)];
            TypeData lbType = new TypeData(type.Name, genericTypes, false, true);
            return (Dictionary<K, V>)ReadHashMap(type, lbType);
        }

        /// <summary> 读取哈希表 | Read hash map </summary>
        private IDictionary ReadHashMap(Type type, TypeData lbType) {
            // 检查对象是否为默认值 | Check if object is default value
            if (ReadBit1()) return null;
            // 读取Count个键值对儿 | Read key-value pairs
            int count = (int)ReadVUInt32();
            Type[] generics = type.GetGenericArguments();
            List<TypeData> lbGenerics = lbType.GenericTypes;
            Type keyType = generics[0];
            Type valueType = generics[1];
            TypeData lbKeyType = lbGenerics[0];
            TypeData lbValueType = lbGenerics[1];
            IDictionary map = ReflectionUtil.CreateHashMap(keyType, valueType, count);
            for (int i = 0; i < count; i++) {
                map.Add(ReadObject(keyType, lbKeyType), ReadObject(valueType, lbValueType));
            }
            return map;
        }

        /// <summary> 读取自定义类型 | Read custom type </summary>
        private object ReadCustom(Type type, TypeData lbType) {
            // 检查对象是否为默认值 | Check if object is default value
            if (ReadBit1()) return lbType.IsStruct ? ReflectionUtil.CreateInstance(type) : null;
            // 读取所有字段的值 | Read all fields value
            object obj = ReflectionUtil.CreateObject(type);
            List<FieldData> lbFields = lbType.Fields;
            IMemberWrapper[] wrappers = ReflectionUtil.GetMembersWrappers(type, lbFields);
            for (int i = 0; i < lbFields.Count; i++) {
                FieldData lbField = lbFields[i];
                TypeData lbFieldType = lbField.Type;
                IMemberWrapper wrapper = wrappers[i];
                try {
                    wrapper.SetValue(obj, ReadObject(wrapper.Type, lbFieldType));
                } catch (Exception ex) {
                    throw new Exception($"LBConvert.ReadCustom({lbType}, {lbField.Name}) error!\n{ex}");
                }
            }
            return obj;
        }
        #endregion

    }

}